home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / DB / odbc.php < prev    next >
PHP Script  |  2004-10-01  |  17KB  |  578 lines

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license,      |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Author: Stig Bakken <ssb@php.net>                                    |
  17. // | Maintainer: Daniel Convissor <danielc@php.net>                       |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: odbc.php,v 1.46 2004/03/11 04:20:11 danielc Exp $
  21.  
  22.  
  23. // XXX legend:
  24. //  More info on ODBC errors could be found here:
  25. //  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/trblsql/tr_err_odbc_5stz.asp
  26. //
  27. // XXX ERRORMSG: The error message from the odbc function should
  28. //                 be registered here.
  29.  
  30.  
  31. require_once 'DB/common.php';
  32.  
  33. /**
  34.  * Database independent query interface definition for PHP's ODBC
  35.  * extension.
  36.  *
  37.  * @package  DB
  38.  * @version  $Id: odbc.php,v 1.46 2004/03/11 04:20:11 danielc Exp $
  39.  * @category Database
  40.  * @author   Stig Bakken <ssb@php.net>
  41.  */
  42. class DB_odbc extends DB_common
  43. {
  44.     // {{{ properties
  45.  
  46.     var $connection;
  47.     var $phptype, $dbsyntax;
  48.     var $row = array();
  49.  
  50.     // }}}
  51.     // {{{ constructor
  52.  
  53.     function DB_odbc()
  54.     {
  55.         $this->DB_common();
  56.         $this->phptype = 'odbc';
  57.         $this->dbsyntax = 'sql92';
  58.         $this->features = array(
  59.             'prepare' => true,
  60.             'pconnect' => true,
  61.             'transactions' => false,
  62.             'limit' => 'emulate'
  63.         );
  64.         $this->errorcode_map = array(
  65.             '01004' => DB_ERROR_TRUNCATED,
  66.             '07001' => DB_ERROR_MISMATCH,
  67.             '21S01' => DB_ERROR_MISMATCH,
  68.             '21S02' => DB_ERROR_MISMATCH,
  69.             '22003' => DB_ERROR_INVALID_NUMBER,
  70.             '22005' => DB_ERROR_INVALID_NUMBER,
  71.             '22008' => DB_ERROR_INVALID_DATE,
  72.             '22012' => DB_ERROR_DIVZERO,
  73.             '23000' => DB_ERROR_CONSTRAINT,
  74.             '23502' => DB_ERROR_CONSTRAINT_NOT_NULL,
  75.             '23503' => DB_ERROR_CONSTRAINT,
  76.             '23505' => DB_ERROR_CONSTRAINT,
  77.             '24000' => DB_ERROR_INVALID,
  78.             '34000' => DB_ERROR_INVALID,
  79.             '37000' => DB_ERROR_SYNTAX,
  80.             '42000' => DB_ERROR_SYNTAX,
  81.             '42601' => DB_ERROR_SYNTAX,
  82.             'IM001' => DB_ERROR_UNSUPPORTED,
  83.             'S0000' => DB_ERROR_NOSUCHTABLE,
  84.             'S0001' => DB_ERROR_ALREADY_EXISTS,
  85.             'S0002' => DB_ERROR_NOSUCHTABLE,
  86.             'S0011' => DB_ERROR_ALREADY_EXISTS,
  87.             'S0012' => DB_ERROR_NOT_FOUND,
  88.             'S0021' => DB_ERROR_ALREADY_EXISTS,
  89.             'S0022' => DB_ERROR_NOSUCHFIELD,
  90.             'S1000' => DB_ERROR_CONSTRAINT_NOT_NULL,
  91.             'S1009' => DB_ERROR_INVALID,
  92.             'S1090' => DB_ERROR_INVALID,
  93.             'S1C00' => DB_ERROR_NOT_CAPABLE
  94.         );
  95.     }
  96.  
  97.     // }}}
  98.     // {{{ connect()
  99.  
  100.     /**
  101.      * Connect to a database and log in as the specified user.
  102.      *
  103.      * @param $dsn the data source name (see DB::parseDSN for syntax)
  104.      * @param $persistent (optional) whether the connection should
  105.      *        be persistent
  106.      *
  107.      * @return int DB_OK on success, a DB error code on failure
  108.      */
  109.     function connect($dsninfo, $persistent = false)
  110.     {
  111.         if (!DB::assertExtension('odbc')) {
  112.             return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  113.         }
  114.  
  115.         $this->dsn = $dsninfo;
  116.         if ($dsninfo['dbsyntax']) {
  117.             $this->dbsyntax = $dsninfo['dbsyntax'];
  118.         }
  119.         switch ($this->dbsyntax) {
  120.             case 'solid':
  121.                 $this->features = array(
  122.                     'prepare' => true,
  123.                     'pconnect' => true,
  124.                     'transactions' => true
  125.                 );
  126.                 break;
  127.             case 'navision':
  128.                 // the Navision driver doesn't support fetch row by number
  129.                 $this->features['limit'] = false;
  130.         }
  131.  
  132.         /*
  133.          * This is hear for backwards compatibility.
  134.          * Should have been using 'database' all along, but used hostspec.
  135.          */
  136.         if ($dsninfo['database']) {
  137.             $odbcdsn = $dsninfo['database'];
  138.         } elseif ($dsninfo['hostspec']) {
  139.             $odbcdsn = $dsninfo['hostspec'];
  140.         } else {
  141.             $odbcdsn = 'localhost';
  142.         }
  143.  
  144.         if ($this->provides('pconnect')) {
  145.             $connect_function = $persistent ? 'odbc_pconnect' : 'odbc_connect';
  146.         } else {
  147.             $connect_function = 'odbc_connect';
  148.         }
  149.         $conn = @$connect_function($odbcdsn, $dsninfo['username'],
  150.                                    $dsninfo['password']);
  151.         if (!is_resource($conn)) {
  152.             return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
  153.                                          null, $this->errorNative());
  154.         }
  155.         $this->connection = $conn;
  156.         return DB_OK;
  157.     }
  158.  
  159.     // }}}
  160.     // {{{ disconnect()
  161.  
  162.     function disconnect()
  163.     {
  164.         $err = @odbc_close($this->connection);
  165.         $this->connection = null;
  166.         return $err;
  167.     }
  168.  
  169.     // }}}
  170.     // {{{ simpleQuery()
  171.  
  172.     /**
  173.      * Send a query to ODBC and return the results as a ODBC resource
  174.      * identifier.
  175.      *
  176.      * @param $query the SQL query
  177.      *
  178.      * @return int returns a valid ODBC result for successful SELECT
  179.      * queries, DB_OK for other successful queries.  A DB error code
  180.      * is returned on failure.
  181.      */
  182.     function simpleQuery($query)
  183.     {
  184.         $this->last_query = $query;
  185.         $query = $this->modifyQuery($query);
  186.         $result = @odbc_exec($this->connection, $query);
  187.         if (!$result) {
  188.             return $this->odbcRaiseError(); // XXX ERRORMSG
  189.         }
  190.         // Determine which queries that should return data, and which
  191.         // should return an error code only.
  192.         if (DB::isManip($query)) {
  193.             $this->manip_result = $result; // For affectedRows()
  194.             return DB_OK;
  195.         }
  196.         $this->row[(int)$result] = 0;
  197.         $this->manip_result = 0;
  198.         return $result;
  199.     }
  200.  
  201.     // }}}
  202.     // {{{ nextResult()
  203.  
  204.     /**
  205.      * Move the internal odbc result pointer to the next available result
  206.      *
  207.      * @param a valid fbsql result resource
  208.      *
  209.      * @access public
  210.      *
  211.      * @return true if a result is available otherwise return false
  212.      */
  213.     function nextResult($result)
  214.     {
  215.         return @odbc_next_result($result);
  216.     }
  217.  
  218.     // }}}
  219.     // {{{ fetchInto()
  220.  
  221.     /**
  222.      * Fetch a row and insert the data into an existing array.
  223.      *
  224.      * Formating of the array and the data therein are configurable.
  225.      * See DB_result::fetchInto() for more information.
  226.      *
  227.      * @param resource $result    query result identifier
  228.      * @param array    $arr       (reference) array where data from the row
  229.      *                            should be placed
  230.      * @param int      $fetchmode how the resulting array should be indexed
  231.      * @param int      $rownum    the row number to fetch
  232.      *
  233.      * @return mixed DB_OK on success, null when end of result set is
  234.      *               reached or on failure
  235.      *
  236.      * @see DB_result::fetchInto()
  237.      * @access private
  238.      */
  239.     function fetchInto($result, &$arr, $fetchmode, $rownum=null)
  240.     {
  241.         $arr = array();
  242.         if ($rownum !== null) {
  243.             $rownum++; // ODBC first row is 1
  244.             if (version_compare(phpversion(), '4.2.0', 'ge')) {
  245.                 $cols = @odbc_fetch_into($result, $arr, $rownum);
  246.             } else {
  247.                 $cols = @odbc_fetch_into($result, $rownum, $arr);
  248.             }
  249.         } else {
  250.             $cols = @odbc_fetch_into($result, $arr);
  251.         }
  252.  
  253.         if (!$cols) {
  254.             /* XXX FIXME: doesn't work with unixODBC and easysoft
  255.                           (get corrupted $errno values)
  256.             if ($errno = @odbc_error($this->connection)) {
  257.                 return $this->RaiseError($errno);
  258.             }*/
  259.             return null;
  260.         }
  261.         if ($fetchmode !== DB_FETCHMODE_ORDERED) {
  262.             for ($i = 0; $i < count($arr); $i++) {
  263.                 $colName = @odbc_field_name($result, $i+1);
  264.                 $a[$colName] = $arr[$i];
  265.             }
  266.             if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  267.                 $a = array_change_key_case($a, CASE_LOWER);
  268.             }
  269.             $arr = $a;
  270.         }
  271.         if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
  272.             $this->_rtrimArrayValues($arr);
  273.         }
  274.         if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
  275.             $this->_convertNullArrayValuesToEmpty($arr);
  276.         }
  277.         return DB_OK;
  278.     }
  279.  
  280.     // }}}
  281.     // {{{ freeResult()
  282.  
  283.     function freeResult($result)
  284.     {
  285.         unset($this->row[(int)$result]);
  286.         return @odbc_free_result($result);
  287.     }
  288.  
  289.     // }}}
  290.     // {{{ numCols()
  291.  
  292.     function numCols($result)
  293.     {
  294.         $cols = @odbc_num_fields($result);
  295.         if (!$cols) {
  296.             return $this->odbcRaiseError();
  297.         }
  298.         return $cols;
  299.     }
  300.  
  301.     // }}}
  302.     // {{{ affectedRows()
  303.  
  304.     /**
  305.      * Returns the number of rows affected by a manipulative query
  306.      * (INSERT, DELETE, UPDATE)
  307.      * @return mixed int affected rows, 0 when non manip queries or
  308.      *               DB error on error
  309.      */
  310.     function affectedRows()
  311.     {
  312.         if (empty($this->manip_result)) {  // In case of SELECT stms
  313.             return 0;
  314.         }
  315.         $nrows = @odbc_num_rows($this->manip_result);
  316.         if ($nrows == -1) {
  317.             return $this->odbcRaiseError();
  318.         }
  319.         return $nrows;
  320.     }
  321.  
  322.     // }}}
  323.     // {{{ numRows()
  324.  
  325.     /**
  326.      * ODBC may or may not support counting rows in the result set of
  327.      * SELECTs.
  328.      *
  329.      * @param $result the odbc result resource
  330.      * @return the number of rows, or 0
  331.      */
  332.     function numRows($result)
  333.     {
  334.         $nrows = @odbc_num_rows($result);
  335.         if ($nrows == -1) {
  336.             return $this->odbcRaiseError(DB_ERROR_UNSUPPORTED);
  337.         }
  338.         return $nrows;
  339.     }
  340.  
  341.     // }}}
  342.     // {{{ quoteIdentifier()
  343.  
  344.     /**
  345.      * Quote a string so it can be safely used as a table / column name
  346.      *
  347.      * Quoting style depends on which dbsyntax was passed in the DSN.
  348.      *
  349.      * Use 'mssql' as the dbsyntax in the DB DSN only if you've unchecked
  350.      * "Use ANSI quoted identifiers" when setting up the ODBC data source.
  351.      *
  352.      * @param string $str  identifier name to be quoted
  353.      *
  354.      * @return string  quoted identifier string
  355.      *
  356.      * @since 1.6.0
  357.      * @access public
  358.      */
  359.     function quoteIdentifier($str)
  360.     {
  361.         switch ($this->dsn['dbsyntax']) {
  362.             case 'access':
  363.                 return '[' . $str . ']';
  364.             case 'mssql':
  365.             case 'sybase':
  366.                 return '[' . str_replace(']', ']]', $str) . ']';
  367.             case 'mysql':
  368.             case 'mysqli':
  369.                 return '`' . $str . '`';
  370.             default:
  371.                 return '"' . str_replace('"', '""', $str) . '"';
  372.         }
  373.     }
  374.  
  375.     // }}}
  376.     // {{{ quote()
  377.  
  378.     /**
  379.      * @deprecated  Deprecated in release 1.6.0
  380.      * @internal
  381.      */
  382.     function quote($str) {
  383.         return $this->quoteSmart($str);
  384.     }
  385.  
  386.     // }}}
  387.     // {{{ errorNative()
  388.  
  389.     /**
  390.      * Get the native error code of the last error (if any) that
  391.      * occured on the current connection.
  392.      *
  393.      * @access public
  394.      *
  395.      * @return int ODBC error code
  396.      */
  397.     function errorNative()
  398.     {
  399.         if (!isset($this->connection) || !is_resource($this->connection)) {
  400.             return @odbc_error() . ' ' . @odbc_errormsg();
  401.         }
  402.         return @odbc_error($this->connection) . ' ' . @odbc_errormsg($this->connection);
  403.     }
  404.  
  405.     // }}}
  406.     // {{{ nextId()
  407.  
  408.     /**
  409.      * Returns the next free id in a sequence
  410.      *
  411.      * @param string  $seq_name  name of the sequence
  412.      * @param boolean $ondemand  when true, the seqence is automatically
  413.      *                           created if it does not exist
  414.      *
  415.      * @return int  the next id number in the sequence.  DB_Error if problem.
  416.      *
  417.      * @internal
  418.      * @see DB_common::nextID()
  419.      * @access public
  420.      */
  421.     function nextId($seq_name, $ondemand = true)
  422.     {
  423.         $seqname = $this->getSequenceName($seq_name);
  424.         $repeat = 0;
  425.         do {
  426.             $this->pushErrorHandling(PEAR_ERROR_RETURN);
  427.             $result = $this->query("update ${seqname} set id = id + 1");
  428.             $this->popErrorHandling();
  429.             if ($ondemand && DB::isError($result) &&
  430.                 $result->getCode() == DB_ERROR_NOSUCHTABLE) {
  431.                 $repeat = 1;
  432.                 $this->pushErrorHandling(PEAR_ERROR_RETURN);
  433.                 $result = $this->createSequence($seq_name);
  434.                 $this->popErrorHandling();
  435.                 if (DB::isError($result)) {
  436.                     return $this->raiseError($result);
  437.                 }
  438.                 $result = $this->query("insert into ${seqname} (id) values(0)");
  439.             } else {
  440.                 $repeat = 0;
  441.             }
  442.         } while ($repeat);
  443.  
  444.         if (DB::isError($result)) {
  445.             return $this->raiseError($result);
  446.         }
  447.  
  448.         $result = $this->query("select id from ${seqname}");
  449.         if (DB::isError($result)) {
  450.             return $result;
  451.         }
  452.  
  453.         $row = $result->fetchRow(DB_FETCHMODE_ORDERED);
  454.         if (DB::isError($row || !$row)) {
  455.             return $row;
  456.         }
  457.  
  458.         return $row[0];
  459.     }
  460.  
  461.     /**
  462.      * Creates a new sequence
  463.      *
  464.      * @param string $seq_name  name of the new sequence
  465.      *
  466.      * @return int  DB_OK on success.  A DB_Error object is returned if
  467.      *              problems arise.
  468.      *
  469.      * @internal
  470.      * @see DB_common::createSequence()
  471.      * @access public
  472.      */
  473.     function createSequence($seq_name)
  474.     {
  475.         $seqname = $this->getSequenceName($seq_name);
  476.         return $this->query("CREATE TABLE ${seqname} ".
  477.                             '(id integer NOT NULL,'.
  478.                             ' PRIMARY KEY(id))');
  479.     }
  480.  
  481.     // }}}
  482.     // {{{ dropSequence()
  483.  
  484.     /**
  485.      * Deletes a sequence
  486.      *
  487.      * @param string $seq_name  name of the sequence to be deleted
  488.      *
  489.      * @return int  DB_OK on success.  DB_Error if problems.
  490.      *
  491.      * @internal
  492.      * @see DB_common::dropSequence()
  493.      * @access public
  494.      */
  495.     function dropSequence($seq_name)
  496.     {
  497.         $seqname = $this->getSequenceName($seq_name);
  498.         return $this->query("DROP TABLE ${seqname}");
  499.     }
  500.  
  501.     // }}}
  502.     // {{{ autoCommit()
  503.  
  504.     function autoCommit($onoff = false)
  505.     {
  506.         if (!@odbc_autocommit($this->connection, $onoff)) {
  507.             return $this->odbcRaiseError();
  508.         }
  509.         return DB_OK;
  510.     }
  511.  
  512.     // }}}
  513.     // {{{ commit()
  514.  
  515.     function commit()
  516.     {
  517.         if (!@odbc_commit($this->connection)) {
  518.             return $this->odbcRaiseError();
  519.         }
  520.         return DB_OK;
  521.     }
  522.  
  523.     // }}}
  524.     // {{{ rollback()
  525.  
  526.     function rollback()
  527.     {
  528.         if (!@odbc_rollback($this->connection)) {
  529.             return $this->odbcRaiseError();
  530.         }
  531.         return DB_OK;
  532.     }
  533.  
  534.     // }}}
  535.     // {{{ odbcRaiseError()
  536.  
  537.     /**
  538.      * Gather information about an error, then use that info to create a
  539.      * DB error object and finally return that object.
  540.      *
  541.      * @param  integer  $errno  PEAR error number (usually a DB constant) if
  542.      *                          manually raising an error
  543.      * @return object  DB error object
  544.      * @see errorNative()
  545.      * @see DB_common::errorCode()
  546.      * @see DB_common::raiseError()
  547.      */
  548.     function odbcRaiseError($errno = null)
  549.     {
  550.         if ($errno === null) {
  551.             switch ($this->dbsyntax) {
  552.                 case 'access':
  553.                     if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
  554.                         $this->errorcode_map['07001'] = DB_ERROR_NOSUCHFIELD;
  555.                     } else {
  556.                         // Doing this in case mode changes during runtime.
  557.                         $this->errorcode_map['07001'] = DB_ERROR_MISMATCH;
  558.                     }
  559.             }
  560.             $errno = $this->errorCode(odbc_error($this->connection));
  561.         }
  562.         return $this->raiseError($errno, null, null, null,
  563.                         $this->errorNative());
  564.     }
  565.  
  566.     // }}}
  567.  
  568. }
  569.  
  570. /*
  571.  * Local variables:
  572.  * tab-width: 4
  573.  * c-basic-offset: 4
  574.  * End:
  575.  */
  576.  
  577. ?>
  578.